<?php

namespace app\user\logic\user;

use app\admin\logic\system\Config as SystemConfig;
use app\admin\model\finance\CreditConfig;
use app\common\exception\LogicException;
use app\common\helper\util\SnowFlake;
use app\common\logic\Payment;
use app\common\model\payment\{PaymentOrder as PO, PaymentOrderItem as POI};
use app\common\model\ProductType;
use app\user\model\user\{Credit as CreditModel};
use Exception;
use think\db\exception\DbException;
use think\facade\{Request};

/**
 * 积分管理
 */
class Credit extends CreditModel
{

    // 用户积分
    public function userCredit()
    {
        return $this->scope('mid')->value('credit') ?: 0;
    }

    // 我的积分页面 - PC
    public function infoAndIncrease(): array
    {
        // 用户积分
        $return['credit'] = $this->userCredit();
        // 支付方式
        $return['payway'] = app(Payment::class)->payway();
        // 充值配置
        $ratio           = SystemConfig::get('credit.recharge', ['ratio' => '1:100'], request()->mhm_id);
        $return['ratio'] = $ratio['ratio'];
        // 余额
        if (in_array('lcnpay', $return['payway'])) {
            $return['balance'] = app(Balance::class)->userBalance();
        }
        // 收入
        if (in_array('income', $return['payway'])) {
            $return['income'] = app(Income::class)->userIncome();
        }

        return $return;
    }

    /**
     * 积分充值 - 生成订单 - PC
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-06-11
     */
    public function recharge($param)
    {
        // 验证数据
        $this->getValidate()->scene('recharge')->failException()->check($param);
        // 计算支付金额
        $ratio_conf = SystemConfig::get('credit.recharge', ['ratio' => '1:100'], request()->mhm_id);
        [$a, $b] = explode(':', $ratio_conf['ratio']);
        $payment = round($a * $param['credit'] / $b, 2);

        if (abs($param['payment'] - $payment) > 0.02) throw new LogicException('参数错误', '金额计算错误');
        // 生成订单
        return $this->createOrder($param);
    }

    /**
     * 创建订单方法 - PC
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-06-08
     */
    protected function createOrder($data)
    {
        $from = strtolower(request()->device_type);
        $this->startTrans();
        try {
            // 用户ID
            $mid = request()->mid;
            // 操作频繁处理
            $update_time = app(POI::class)
                ->where('user_id', $mid)
                ->where('product_type_id', ProductType::getProductIdByKey('credit'))
                ->order('order_no', 'DESC')
                ->value('update_time');

            if (($update_time + 5) > Request::time()) throw new LogicException('操作过于频繁,请稍后再试');
            // 订单号
            $PD = app(SnowFlake::class)->createPD();
            $PI = app(SnowFlake::class)->createPI();
            // 主订单
            $order = app(PO::class)::create([
                'order_no'   => $PD,
                'user_id'    => $mid,
                'from'       => $from,
                'mhm_id'     => request()->user->mhm_id,
                'payment'    => $data['payment'],
                'status'     => '10',
                'close_time' => time() + 30 * 60,
            ]);

            if ($order->isEmpty()) throw new LogicException('订单生成失败', '主订单生成失败');
            // 子订单
            $order_item = app(POI::class)::create([
                'order_no'        => $PD,
                'order_sub_no'    => $PI,
                'user_id'         => $mid,
                'from'            => $from,
                'mhm_id'          => request()->user->mhm_id,
                'product_id'      => 0,
                'product_type_id' => ProductType::getProductIdByKey('credit'),
                'payment'         => $data['payment'],
                'original_price'  => $data['credit'],
            ]);
            if ($order_item->isEmpty()) throw new LogicException('订单生成失败', '子订单生成失败');

            $this->commit();
        } catch (LogicException $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }

        unset($order->id);
        return $order;
    }

    /**
     * 充值积分 - PC
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-08-08
     */
    public function increaseUserCredit($mid, $type, $num = 0, $rel_type = '', $rel_pk = '', $note = '')
    {
        $this->startTrans();
        try {

            $credit = $this->where('user_id', $mid)->findOrEmpty();

            if ($credit->isEmpty()) {
                // 添加余额
                $credit          = new Credit;
                $credit->user_id = $mid;
                $credit->credit  = $num;
            } else {
                // 变更余额
                $credit->credit += $num;
            }
            if (!$credit->save()) throw new LogicException('操作失败');

            // 流水记录
            app(CreditFlow::class)->updateFlow($mid, $type, $num, $credit->credit, $rel_type, $rel_pk, $note);
            $this->commit();

            return $credit;
        } catch (LogicException|Exception $e) {

            $this->rollback();
            throw new LogicException($e->getMessage());

        }
    }

    /**
     * 用户积分主页
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-23
     *
     * @throws DbException
     */
    public function base(): array
    {
        // 用户积分
        $credit = $this->userCredit();
        // 是否签到
        $punch_in = app(PunchIn::class)->hasTodayPunchIn();
        // 积分明细
        $flow = app(CreditFlow::class)->flow(['alter_type', 'rel_type_text']);

        return ['credit' => $credit, 'today_punch_in' => $punch_in, 'flow' => $flow];
    }

    /**
     * 用户积分签到
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-23
     *
     * @throws DbException
     */
    public function signIn(): array
    {
        // 签到
        $sign_in = $this->doSignIn();
        if (!$sign_in) throw new LogicException('签到失败..');
        // 积分明细
        $flow = app(CreditFlow::class)->flow(['rel_type_text']);

        return ['credit' => $sign_in->credit, 'today_punch_in' => 1, 'flow' => $flow];
    }

    /**
     * 签到操作
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-23
     *
     */
    public function doSignIn()
    {
        $mid = request()->mid;
        // 是否有签到记录
        $record = app(PunchIn::class)->hasRecord(true);
        $this->startTrans();
        try {
            if ($record->isEmpty()) {
                $re = PunchIn::create([
                    'user_id' => $mid,
                    'days'    => 1,
                ]);
                if ($re->isEmpty()) throw new LogicException('签到失败');
                $add = $this->addCredit($re->id);
            } else {
                // 今天是否签到
                $today = app(PunchIn::class)->hasTodayPunchIn();
                // 昨天是否签到
                $yesterday = app(PunchIn::class)->hasYesterdayPunchIn(true);
                if ($today) throw new LogicException('已签到');

                $record->days        = $yesterday->isEmpty() ? 1 : ($yesterday->days + 1);
                $record->update_time = time();
                if (!$record->save()) throw new LogicException('签到失败.');
                $add = $this->addCredit($record->id);
            }
            $this->commit();
            return $add;
        } catch (LogicException|Exception $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }
    }

    /**
     * 签到积分添加
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-23
     *
     * @param [type] $rel_pk [description]
     */
    public function addCredit($rel_pk)
    {
        // 参数
        $mid = request()->mid;

        if (!$mid) throw new LogicException('签到失败');
        // 获取积分规则
        $score = app(CreditConfig::class)->where('key', 'sign_in')->where('mhm_id', request()->mhm_id)->value('score');
        if (!$score) throw new LogicException('签到失败');
        // 添加积分
        return $this->increaseUserCredit($mid, 4, $score, 'sign_in', $rel_pk, '签到获得的积分');
    }

    public function getRatio(): string
    {
        $redeem = SystemConfig::get('credit.redeem', ['ratio' => '1:0'], request()->mhm_id);
        $ratio  = explode(':', $redeem['ratio']);

        return strval($ratio[1]);
    }

    /**
     * 获取用户积分抵扣数组
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-24
     *
     */
    public function getUserCreditRedeemArr(int $price, $order_price = 0): array
    {
        $credit = $this->userCredit();

        if ($credit <= 0 || !$price) return [];

        $ratio     = SystemConfig::get('credit.redeem', [], request()->mhm_id);
        $order     = SystemConfig::get('credit.order', [], request()->mhm_id);
        $threshold = SystemConfig::get('credit.threshold', [], request()->mhm_id);

        if (!count($ratio) || !count($order) || !count($threshold) || $order_price < $order['order'] || $credit < $threshold['threshold']) return [];

        [$a, $b] = explode(':', $ratio['ratio']);

        $maxNum = intval(bcdiv(bcmul($credit, $a), $b));
        if (!$maxNum) return [];
        $maxNum    = min($maxNum, $price);
        $maxCredit = intval(bcdiv(bcmul($maxNum, $b), $a));

        $arr[] = ['credit' => $maxCredit, 'num' => $maxNum];

        $num = bcdiv($maxNum, 10);

        if (!$num) return $arr;

        $base = intval(bcdiv(bcmul(10, $b), $a));

        for ($i = 1; $i <= $num; $i++) {
            if (bcmul($i, 10) != $maxNum) {
                $arr[] = ['credit' => intval(bcmul($i, $base)), 'num' => intval(bcmul($i, 10))];
            }
        }
        return $arr;
    }

    /**
     * 积分抵扣
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-25
     *
     * @param integer $credit [description]
     */
    public function redeemCredit(int $credit = 0, $rel_pk = '', $type = 7)
    {
        $data         = $this->scope('mid')->findOrEmpty();
        $data->credit -= $credit;
        if (!$data->save()) throw new LogicException('积分抵扣失败');
        // 添加流水
        $create = [
            'user_id'  => request()->mid,
            'type'     => $type,
            'num'      => $credit,
            'credit'   => $data->credit,
            'rel_type' => 'redeem',
            'rel_pk'   => $rel_pk,
            'note'     => '购买产品抵扣积分',
        ];

        $add = CreditFlow::create($create);
        if ($add->isEmpty()) throw new LogicException('积分流水添加失败');
    }

    /**
     * 订单积分退还
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-08-25
     *
     */
    public function returnOrderCredit($order)
    {
        // 订单抵扣产生的流水
        $flow = app(CreditFlow::class)->where('rel_pk', $order->order_no)->findOrEmpty();

        if ($flow->isEmpty()) {   // 不存在，可能数据丢失，按订单处理
            foreach ($order->redeemCreditSub as $order_item) {
                $num = $order_item->credit->credit;
                $mid = $order_item->user_id;
                $num && $this->_returnCredit($mid, $num, $order_item->order_sub_no);
            }
        } else {    // 存在订单抵扣产生的流水，按抵扣流水处理
            $num = $flow->num;
            $mid = $flow->user_id;
            $num && $this->_returnCredit($mid, $num, $flow->rel_pk);
        }
    }

    private function _returnCredit($mid, $num, $rel_pk)
    {
        $data         = $this->scope('mid', $mid)->findOrEmpty();
        $data->credit += $num;
        if (!$data->save()) throw new LogicException('积分退还失败');

        // 添加流水
        $create = [
            'user_id'  => $mid,
            'type'     => 8,
            'num'      => $num,
            'credit'   => $data->credit,
            'rel_type' => 'redeem',
            'rel_pk'   => $rel_pk,
            'note'     => '订单抵扣退还',
        ];

        $add = CreditFlow::create($create);
        if ($add->isEmpty()) throw new LogicException('积分流水添加失败');
    }

    /**
     * 获取商品积分+现金
     * Author: 亓官雨树 <lucky.max@foxmail.com>
     * Date: 22/11/22
     *
     * @return array
     */
    // public function getUserProdCreditCash($cash, $cred): array
    public function getUserProdCreditCash(): array
    {
        $credit = $this->userCredit();
        $redeem = SystemConfig::get('credit.redeem', [], request()->mhm_id);
        if (!($redeem['ratio'] ?? null)) throw new LogicException('积分兑换比例配置错误，请联系商家');
        $ratio = explode(':', $redeem['ratio']) ?? null;

        $ratio = $ratio ? end($ratio) : null;
        return ['user_credit' => $credit, 'ratio' => intval($ratio)];
        // 最高抵扣积分与可抵扣积分差 之最小数
        // $min = $credit < $cred ? ceil(($cred - $credit) / $b) : 0;
        // 最高抵扣积分与比例配置 之配置下最大选项数量
        // $num = floor(bcdiv(min($credit, $cred), $b));
        // $num = $min ? $num + $min - 1 : $num;

        // $arr = [];
        // for ($i = $min; $i <= $num; $i++) {
        //     $arr[] = ['credit' => intval(bcsub($cred, bcmul($i, $b))), 'cash' => floatval(bcadd($cash, bcmul($i, $a), 2))];
        // }

        // return ['user_credit' => $credit, 'max_credit' => current($arr)['credit'], 'ratio' => intval($b), 'arr' => $arr];
    }

}
